package com.egg.mall.service.sku.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.egg.common.core.constant.StringPool;
import com.egg.common.core.exception.CustomException;
import com.egg.common.mybatis.service.impl.BaseServiceImpl;
import com.egg.common.util.IdGenUtil;
import com.egg.common.util.JsonUtil;
import com.egg.mall.entity.sku.Sku;
import com.egg.mall.mapper.sku.SkuMapper;
import com.egg.mall.model.request.SkuPut;
import com.egg.mall.model.request.SkuStockQuantityPut;
import com.egg.mall.model.request.SpuPostOrPut;
import com.egg.mall.service.sku.ISkuService;
import com.egg.mall.service.spu.ISpuAttributeService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


/**
 * @author east
 * @since 2022-08-08
 */
@Slf4j
@Service
public class SkuServiceImpl extends BaseServiceImpl<SkuMapper, Sku> implements ISkuService {


    @Transactional(rollbackFor = Throwable.class)
    @Override
    public void saveSku(Serializable spuId, List<SpuPostOrPut.Sku> list, Map<String, Long> specIdMap) {
        // 保存ID
        List<Long> saveId = list.stream()
                .filter(o -> ObjectUtil.isNotEmpty(o.getId()))
                .map(o -> Long.parseLong(o.getId())).collect(Collectors.toList());
        // 已存在的ID
        List<Long> existId = findBySpuId(spuId).stream().map(o -> o.getId()).collect(Collectors.toList());
        // 需要删除的ID
        List<Long> delId = existId.stream().filter(o -> !saveId.contains(o)).collect(Collectors.toList());
        this.baseDel(delId);
        // 赋值
        List<Sku> skuList = list.stream().map(temp -> {
            Sku sku = JsonUtil.objToObj(temp, Sku.class);
            if (!saveId.contains(sku.getId())) {
                // 如果非数据库中的id,视为无效ID,直接舍弃
                sku.setId(null);
            }
            String specIds = Arrays.stream(temp.getSpecIds().split(StringPool.COMMA))
                    .map(specId -> specId.startsWith(ISpuAttributeService.SPEC_TEMP_ID_PREFIX) ? specIdMap.get(specId).toString() : specId)
                    .collect(Collectors.joining(StringPool.COMMA));
            sku.setSpecIds(specIds);
            sku.setSpuId(Long.parseLong(spuId.toString()));
            // 设置版本号,方便下单的时候进行快照处理
            // skuId + version
            // 如果存在,直接使用快照信息
            // 如果没有,则需要新增一个快照
            sku.setSnapshotVersion(IdGenUtil.longId());
            return sku;
        }).collect(Collectors.toList());
        baseSave(skuList);
    }

    @Override
    public List<Sku> findBySpuId(Serializable spuId) {
        if (ObjectUtil.isEmpty(spuId)) {
            return Collections.emptyList();
        }
        return findBySpuId(Collections.singletonList(spuId));
    }

    @Override
    public List<Sku> findBySpuId(Collection<? extends Serializable> spuId) {
        if (ObjectUtil.isEmpty(spuId)) {
            return Collections.emptyList();
        }
        LambdaQueryWrapper<Sku> qw = buildLambdaQw();
        qw.in(Sku::getSpuId, spuId);
        return list(qw);
    }

    @Transactional(rollbackFor = Throwable.class)
    @Override
    public void delBySpuId(Collection<? extends Serializable> spuId) {
        if (ObjectUtil.isEmpty(spuId)) {
            return;
        }
        LambdaQueryWrapper<Sku> qw = buildLambdaQw();
        qw.in(Sku::getSpuId, spuId);
        remove(qw);
    }

    @Transactional(rollbackFor = Throwable.class)
    @Override
    public Long edit(Serializable id, SkuPut param) {
        Sku exist = baseGetById(id);
        Sku sku = JsonUtil.objToObj(param, Sku.class);
        sku.setId(exist.getId());
        baseSave(sku);
        return sku.getId();
    }

    /**
     * 更新库存, 基于乐观锁version版本
     * -- 最大更新一百次
     *
     * @param list
     */
    @Transactional(rollbackFor = Throwable.class)
    @Override
    public void optimisticAddStockQuantity(List<SkuStockQuantityPut> list) {
        if (ObjectUtil.isEmpty(list)) {
            return;
        }
        // 合并更新
        Map<Long, Integer> idQuantity = list.stream()
                .collect(Collectors.groupingBy(o -> o.getId(), Collectors.summingInt(o -> o.getQuantity())));
        int maxCount = 99;
        for (Map.Entry<Long, Integer> entry : idQuantity.entrySet()) {
            boolean jump;
            int initCount = 0;
            do {
                if (initCount > maxCount) {
                    // 并发实在过高, 就丢弃一次统计
                    log.warn("更新SKU {} 库存失败,并发过高", entry.getKey());
                    return;
                }
                Sku sku = baseGetById(entry.getKey());
                int remaining = sku.getStockQuantity() + entry.getValue();
                if (remaining < 0) {
                    // 由于内部一直为加, 如果外部扣减的数量过多, 这里抛出异常, 不允许库存为负数
                    throw new CustomException("库存不足");
                }
                Sku up = new Sku();
                up.setId(sku.getId());
                up.setStockQuantity(remaining);
                up.setVersion(sku.getVersion());
                jump = !updateById(up);
                initCount++;
            } while (jump);
        }
    }


}
